# -*- coding: utf-8 -*-
# Estructura base de la aplicación. Contiene la instanciación de todos los
# paneles de la aplicación.

## vipera, un diseñador de aplicaciones para Python
##     Copyright (C) 2011 Ángel Luis García García
##
##    This program is free software: you can redistribute it and/or modify
##    it under the terms of the GNU General Public License as published by
##    the Free Software Foundation, either version 3 of the License, or
##    (at your option) any later version.
##
##    This program is distributed in the hope that it will be useful,
##    but WITHOUT ANY WARRANTY; without even the implied warranty of
##    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
##    GNU General Public License for more details.
##
##    You should have received a copy of the GNU General Public License
##    along with this program.  If not, see <http://www.gnu.org/licenses/>.

import wx
import wx.py.shell as py
import wx.aui
import wx.lib.scrolledpanel as scrolled
import wx.lib.agw.toasterbox as TB

import os
import time

from conf_proyecto_vipera import conf_vipera
from packages_vipera import panel_p, dialogo_package_p
from editor_vipera import panel_codigo_p
from doc_modulo_vipera import panel_doc_mod
from constantes_vipera import panel_cte_p, panel_cte_dicc_p
from imports_vipera import panel_import_p
from estructuras_vipera import panel_estruc_p, hoja_estruc_p
from codigo_modulo_vipera import codigo_modulo as codigo_modulo_actual
from ingenieria_inversa_vipera import panel_principal as panel_ing_inversa
from configuracion_vipera import panel_conf_vipera_p, proy_por_defecto
from operaciones_proy_vista import panel_oper_proy
from frontend_vista import m_panel_principal
from clases_design_ogl_vipera import panel_design_class, MyEvtHandler
from clases_design_conf_vipera import panel_nombre_clase, panel_param_inst
from clases_design_conf_vipera import panel_atrib_clase, panel_metodos_p
from clases_design_conf_vipera import panel_prop_clase, panel_coment_clase
from clases_design_conf_vipera import panel_var_clase_p, panel_const_p, panel_dest_p
from funciones_vipera import panel_funciones as panel_param_func
from editor2_vipera import panel_codigo_ed
from clases_design_ogl_vipera import MyEvtHandler_p
from cod_prin_vipera import panel_prin_p
from clases_modelo import modelo_clases, modelo_relaciones
from clases_modelo import modelo_relaciones_dependencia, modelo_texto_ogl
from i18n_vipera import t
from operaciones_proy_vipera import panel_oper_proy_p

from salir_vista import dialogo_salir

class dialogo_salir_p(dialogo_salir):
    '''Dialogo para salir de la aplicación'''
    def __init__(self, parent, idioma = None):
        '''Constructor de clase'''
        dialogo_salir.__init__(self, parent)
        # Cambiamos idioma.
        self.SetTitle(t(u'Salir de vipera', idioma))
        self.m_checkBox_salvar.SetLabel(t(u'Salvar proyecto', idioma))
      
class panel_principal(m_panel_principal):
    '''Panel del diseñador de aplicaciones'''
    def __init__(self, parent, datos_conf, idioma = None):
        '''Constructor de clase'''        
        m_panel_principal.__init__(self, parent)
        # Idioma.
        self.l = idioma
        # Cambiamos idioma de los widgets generados en wxFB.
        self.m_listbook_designer.SetPageText(0,t(u'Proyecto', self.l))
        self.m_listbook_designer.SetPageText(1,t(u'Módulos', self.l))
        self.m_listbook_designer.SetPageText(2,t(u'Código Python', self.l))
        self.m_listbook_designer.Refresh()
        self.m_notebook_modulos.SetPageText(0,t(u'Clases', self.l))
        self.m_notebook_modulos.SetPageText(1,t(u'Funciones', self.l))
        self.m_notebook_modulos.SetPageText(2,t(u'Constantes', self.l))
        self.m_notebook_modulos.SetPageText(3,t(u'Registros', self.l))
        self.m_notebook_modulos.SetPageText(5,t(u'Documentación', self.l))
        self.m_notebook_modulos.SetPageText(6,t(u'Código principal', self.l))
        self.m_listbook_carac_clases.SetPageText(0, t(u'Definición', self.l))
        self.m_listbook_carac_clases.SetPageText(1, t(u'Parámetros', self.l))
        self.m_listbook_carac_clases.SetPageText(2, t(u'Atributos', self.l))
        self.m_listbook_carac_clases.SetPageText(3, t(u'Métodos', self.l))
        self.m_listbook_carac_clases.SetPageText(4, t(u'Propiedades', self.l))
        self.m_listbook_carac_clases.SetPageText(5, t(u'Documentación', self.l))
        self.m_listbook_carac_clases.SetPageText(6, t(u'Constantes', self.l))
        self.m_listbook_carac_clases.SetPageText(7, t(u'Constructor', self.l))
        self.m_listbook_carac_clases.SetPageText(8, t(u'Destructor', self.l))
        self.m_bpButton_expandir_panel_der.SetToolTipString(t(u'Expandir panel derecho', self.l))
        self.m_bpButton_expandir_panel_izq.SetToolTipString(t(u'Expandir panel izquierdo', self.l))
        self.m_bpButton_crear_clase.SetToolTipString(t(u'Crear clase', self.l))
        self.m_bpButton_renombrar_clase.SetToolTipString(t(u'Renombrar clase', self.l))
        self.m_bpButton_eliminar_clase.SetToolTipString(t(u'Eliminar clase', self.l))
        self.m_bpButton_orden_creacion.SetToolTipString(t(u'Orden de creación de clases', self.l))
        self.m_bpButton_crear_relacion.SetToolTipString(t(u'Crear relación entre clases', self.l))
        self.m_bpButton_eliminar_relacion.SetToolTipString(t(u'Eliminar relación entre clases', self.l))
        self.m_bpButton_crear_texto.SetToolTipString(t(u'Crear texto', self.l))
        self.m_bpButton_modificar_texto.SetToolTipString(t(u'Modificar texto', self.l))
        self.m_bpButton_eliminar_texto.SetToolTipString(t(u'Eliminar texto', self.l))
        self.m_button_crear_modulo.SetLabel(t(u'Crear', self.l))
        self.m_button_renombrar_modulo.SetLabel(t(u'Renombrar', self.l))
        self.m_button_eliminar_modulo.SetLabel(t(u'Eliminar', self.l))
        self.m_button_crear_func.SetLabel(t(u'Crear', self.l))
        self.m_button_renombrar_func.SetLabel(t(u'Renombrar', self.l))   
        self.m_button_eliminar_func.SetLabel(t(u'Eliminar', self.l))
        self.m_bpButton_expandir_panel_der_func.SetToolTipString(t(u'Expandir panel derecho', self.l))
        self.m_bpButton_expandir_panel_izq_func.SetToolTipString(t(u'Expandir panel izquierdo', self.l))
        # ########################
        # Iconos de la aplicación.
        # ########################
        il = wx.ImageList(48, 48)
        imagenes = [u'recursos/images/config_proyecto_vipera.png',
                    u'recursos/images/modulos_vipera.png',
                    u'recursos/images/codigo_python_vipera.png',
                    u'recursos/images/packages_vipera.png']
        for imagen in imagenes :
            bmp = wx.Bitmap(imagen, wx.BITMAP_TYPE_ANY)
            il.Add(bmp)
        self.m_listbook_designer.AssignImageList(il)
        self.m_listbook_designer.SetPageImage(0,0)
        self.m_listbook_designer.SetPageImage(1,1)
        self.m_listbook_designer.SetPageImage(2,2)
        self.m_listbook_designer.SetPageImage(3,3)
        # ####################################
        # Iconos del área de diseño de clases.
        # ####################################
        il = wx.ImageList(48, 48)
        imagenes = [u'recursos/images/definicion_vipera.png',
                    u'recursos/images/parametro_vipera.png',
                    u'recursos/images/atributo_vipera.png',
                    u'recursos/images/metodo_vipera.png',
                    u'recursos/images/propiedad_vipera.png',
                    u'recursos/images/informacion_vipera.png',
                    u'recursos/images/constante_vipera.png',
                    u'recursos/images/constructor_vipera.png',
                    u'recursos/images/destructor_vipera.png']
        for imagen in imagenes :
            bmp = wx.Bitmap(imagen, wx.BITMAP_TYPE_ANY)
            il.Add(bmp)
        self.m_listbook_carac_clases.AssignImageList(il)
        self.m_listbook_carac_clases.SetPageImage(0,0)
        self.m_listbook_carac_clases.SetPageImage(1,1)
        self.m_listbook_carac_clases.SetPageImage(2,2)
        self.m_listbook_carac_clases.SetPageImage(3,3)
        self.m_listbook_carac_clases.SetPageImage(4,4)
        self.m_listbook_carac_clases.SetPageImage(5,5)
        self.m_listbook_carac_clases.SetPageImage(6,6)
        self.m_listbook_carac_clases.SetPageImage(7,7)
        self.m_listbook_carac_clases.SetPageImage(8,8)
        # Estructura interna.
        self.lista_modulos = list()
        '''
        x_lista_modulos.append([nombre_modulo, [documentacion, imports, registros, 
                                                constantes, funciones, clases, codigo_modulo]])
        '''
        # #######################################
        # Ensamblado y definición de componentes.
        # #######################################

        # Panel de configuración de vipera.
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.panel_conf_vipera = conf_vipera(self.m_panel_proyecto, self, idioma = idioma)
        sizer.Add(self.panel_conf_vipera,1,wx.ALL|wx.EXPAND,1)
        self.m_panel_proyecto.SetSizer(sizer)

        # Panel de editor de código generado Python.
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.panel_editor = panel_editor(self.m_panel_generador_codigo,
                                         self, idioma = idioma)
        sizer.Add(self.panel_editor,1,wx.ALL|wx.EXPAND,1)
        self.m_panel_generador_codigo.SetSizer(sizer)

        # Panel de packages.
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.panel_packages = panel_package(self.m_panel_packages, self, idioma = idioma) 
        sizer.Add(self.panel_packages,1,wx.ALL|wx.EXPAND,1)
        self.m_panel_packages.SetSizer(sizer)

        # Panel de diseño OGL.
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.panel_ogl = panel_design_class(self.m_panel_ogl, MyEvtHandler_p, [self, self], self, idioma = idioma)
        # Incluimos configuración.
        self.panel_ogl.panel.configurar_canvas(datos_conf['alto_ogl'],
                                               datos_conf['ancho_ogl'],
                                               datos_conf['color_back_ogl'])
        sizer.Add(self.panel_ogl,1,wx.ALL|wx.EXPAND,1)
        self.m_panel_ogl.SetSizer(sizer)

        # Panel de definición de clase (nombre, hereda de, ...)
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.panel_def_clases = panel_nombre_clase(self.m_panel_def_clases, self, idioma = idioma)
        sizer.Add(self.panel_def_clases,1,wx.ALL|wx.EXPAND,1)
        self.m_panel_def_clases.SetSizer(sizer)

        # Panel de parámetros de instanciación de clase.
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.panel_param_clases = panel_param_inst(self.m_panel_param_clases, self, 
                                                   proy_por_defecto['nfilas'], idioma = idioma)
        sizer.Add(self.panel_param_clases,1,wx.ALL|wx.EXPAND,1)
        self.m_panel_param_clases.SetSizer(sizer)

        # Panel de atributos de clase.
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.panel_atrib_clases = panel_atrib_clase(self.m_panel_atrib_clases, self,
                                                    proy_por_defecto['nfilas'], idioma = idioma)
        sizer.Add(self.panel_atrib_clases,1,wx.ALL|wx.EXPAND,1)
        self.m_panel_atrib_clases.SetSizer(sizer)

        # Panel de métodos de clase.
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.panel_met_clases = panel_metodos_p(self.m_panel_met_clases,
                                                self,
                                                proy_por_defecto['nfilas'], idioma = idioma) 
        sizer.Add(self.panel_met_clases,1,wx.ALL|wx.EXPAND,1)
        self.m_panel_met_clases.SetSizer(sizer)

        # Panel de propiedades de clase.
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.panel_prop_clases = panel_prop_clase(self.m_panel_prop_clases, self,
                                                  proy_por_defecto['nfilas'], idioma = idioma)
        sizer.Add(self.panel_prop_clases,1,wx.ALL|wx.EXPAND,1)
        self.m_panel_prop_clases.SetSizer(sizer)

        # Panel de información de clase.
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.panel_info_clases = panel_coment_clase(self.m_panel_info_clases, self, idioma = idioma)
        sizer.Add(self.panel_info_clases,1,wx.ALL|wx.EXPAND,1)
        self.m_panel_info_clases.SetSizer(sizer)

        # Panel de constantes de clase.
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.panel_cte_clases = panel_var_clase_p(self.m_panel_cte_clases, self, idioma = idioma)
        sizer.Add(self.panel_cte_clases,1,wx.ALL|wx.EXPAND,1)
        self.m_panel_cte_clases.SetSizer(sizer)

        # Panel de código constructor de clase.
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.panel_const_clases = panel_const_p(self.m_panel_const_clases, self, idioma = idioma)
        sizer.Add(self.panel_const_clases,1,wx.ALL|wx.EXPAND,1)
        self.m_panel_const_clases.SetSizer(sizer)

        # Panel de código destructor de clase.
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.panel_dest_clases = panel_dest_p(self.m_panel_dest_clases, self, idioma = idioma)
        sizer.Add(self.panel_dest_clases,1,wx.ALL|wx.EXPAND,1)
        self.m_panel_dest_clases.SetSizer(sizer)

        # Panel de editor de funciones de módulo.
        sizer = wx.BoxSizer(wx.VERTICAL)
        indentacion = 4
        self.panel_editor_func = panel_codigo_ed(self.m_panel_der_func, indentacion, idioma = idioma)
        sizer.Add(self.panel_editor_func,1,wx.ALL|wx.EXPAND,1)
        self.m_panel_der_func.SetSizer(sizer)

        # Panel de parámetros de funciones de módulo.
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.panel_param_func = panel_param_func(self.m_panel_izq_func,
                                                 self,
                                                 proy_por_defecto['nfilas'], idioma = idioma)
        sizer.Add(self.panel_param_func,1,wx.ALL|wx.EXPAND,1)
        self.m_panel_izq_func.SetSizer(sizer)

        # Panel de constantes de módulo.
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.panel_ctes = panel_cte_p(self.m_panel_ctes, idioma = idioma) 
        sizer.Add(self.panel_ctes,1,wx.ALL|wx.EXPAND,1)
        self.m_panel_ctes.SetSizer(sizer)

        # Panel de registros de módulo.
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.panel_registros = panel_estruc_p(self.m_panel_registros, idioma = idioma) 
        sizer.Add(self.panel_registros,1,wx.ALL|wx.EXPAND,1)
        self.m_panel_registros.SetSizer(sizer)

        # Panel de imports de módulo.
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.panel_imports = panel_import_p(self.m_panel_imports, idioma = idioma) 
        sizer.Add(self.panel_imports,1,wx.ALL|wx.EXPAND,1)
        self.m_panel_imports.SetSizer(sizer)

        # Panel de documentación de módulo.
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.panel_doc = panel_doc_mod(self.m_panel_doc, idioma = idioma) 
        sizer.Add(self.panel_doc,1,wx.ALL|wx.EXPAND,1)
        self.m_panel_doc.SetSizer(sizer)

        # Panel de código principal de módulo.
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.panel_cod_prin = panel_prin_p(self.m_panel_cod_prin, idioma = idioma) 
        sizer.Add(self.panel_cod_prin,1,wx.ALL|wx.EXPAND,1)
        self.m_panel_cod_prin.SetSizer(sizer)

        # Bindings de clases.
        self.m_bpButton_crear_clase.Bind(wx.EVT_BUTTON, self.panel_ogl.OnCrearClase)
        self.m_bpButton_renombrar_clase.Bind(wx.EVT_BUTTON, self.panel_ogl.OnRenombrarClase)
        self.m_bpButton_eliminar_clase.Bind(wx.EVT_BUTTON, self.panel_ogl.OnEliminarClase)
        self.m_bpButton_orden_creacion.Bind(wx.EVT_BUTTON, self.panel_ogl.OnOrdenCreacion)
        self.m_bpButton_crear_relacion.Bind(wx.EVT_BUTTON, self.panel_ogl.OnCrearRelacion)
        self.m_bpButton_eliminar_relacion.Bind(wx.EVT_BUTTON, self.panel_ogl.OnEliminarRelacion)
        self.m_bpButton_crear_texto.Bind(wx.EVT_BUTTON, self.panel_ogl.OnCrearTexto)
        self.m_bpButton_modificar_texto.Bind(wx.EVT_BUTTON, self.panel_ogl.OnModificarTexto)
        self.m_bpButton_eliminar_texto.Bind(wx.EVT_BUTTON, self.panel_ogl.OnEliminarTexto)

        # Bindings de funciones.
        self.m_button_crear_func.Bind(wx.EVT_BUTTON, self.panel_param_func.OnCrearFuncion)
        self.m_button_renombrar_func.Bind(wx.EVT_BUTTON, self.panel_param_func.OnRenombrarFuncion)
        self.m_button_eliminar_func.Bind(wx.EVT_BUTTON, self.panel_param_func.OnEliminarFuncion)
        self.m_choice_funciones.Bind(wx.EVT_CHOICE, self.panel_param_func.OnElegirFuncion)
        self.m_choice_funciones.Bind(wx.EVT_CHOICE, self.panel_param_func.OnElegirFuncion)
        self.m_choice_funciones.Bind(wx.EVT_SET_FOCUS, self.panel_param_func.OnSetFocusChoiceFuncion)

        # Datos de documentación al inicializar.
        self.panel_conf_vipera.cargar_datos_extra_proyecto()

    def OnUpdateModulo(self, event):
        '''Activar/desactivar áreas de diseño de módulos'''
        modulo = self.m_choice_modulos.GetStringSelection()
        if len(modulo.strip()) != 0:
            self.m_notebook_modulos.Enable(True)
            return
        if self.m_notebook_modulos.IsEnabled():
            # Desactivamos paneles.
            self.m_notebook_modulos.Enable(False)
            # Limpiamos.
            self.limpiar_widget_modulo()

    def ayuda_toasterbox(self, posicion_x, posicion_y, texto, titulo):
        '''Función de ayuda ToasterBox'''
        # En Linux el toasterbox no funciona muy bien...
        if os.name == 'posix': return #  scrollType = TB.TB_SCR_TYPE_DU
        if os.name == 'nt': scrollType = TB.TB_SCR_TYPE_FADE
        tb = TB.ToasterBox(self, TB.TB_SIMPLE, TB.TB_SIMPLE,
                           TB.TB_ONTIME, scrollType = scrollType)
        tb.SetTitle(titulo)
        tb.SetPopupSize((200, 150))
        tb.SetPopupPosition((posicion_x, posicion_y))
        tb.SetPopupPauseTime(5000)
        tb.SetPopupScrollSpeed(20)
        tb.SetPopupText(texto)
        tb.Play()

    def OnPageChangedListBookDesigner(self, event):
        '''Event Handler de cambio de paneles del diseñador'''
        # Salvamos lo que está en edición, ¡siempre!.
        self.salvar_ultimos_cambios()
        self.guardar_datos_modulo()
        # Obtenemos datos de la hoja en la que estamos y el nombre del módulo actual.
        nhoja = self.m_listbook_designer.GetSelection()
        modulo = self.m_choice_modulos.GetStringSelection()
        # Panel de configuración de código y hay módulo en edición.
        if nhoja == 2 and len(modulo.strip()) != 0:
            # Generamos de forma automática el código Python asociado al módulo.
            self.panel_editor.generar_codigo_python()
        # Panel de módulos y no hay módulo en edición.
        if nhoja == 1 and len(modulo.strip()) == 0:
            msj = u"Para comenzar haga click en 'Crear' para crear un módulo" + \
                u" y a continuación selecciónelo del combo"
            msj = t(msj, self.l)
            x, y, x1, y1 = self.GetScreenRect()
            self.ayuda_toasterbox(x, y1, msj, t(u"Ayuda vipera", self.l))
        # Panel de configuración de proyecto.
        try:
            if nhoja == 0:
                # Cargamos nombres de módulos en área de documentación.
                self.panel_conf_vipera.cargar_nombres_modulos_en_doc()
                # ...y no hay nombre de proyecto.
                if len(self.panel_conf_vipera.m_textCtrl_nproy.GetValue().strip()) == 0:
                    msj = u"Para salvar el proyecto es necesario incluir como mínimo" + \
                        u" un nombre de proyecto en la 'Pestaña Definición' campo 'Nombre de proyecto'"
                    msj = t(msj, self.l)
                    x, y, x1, y1 = self.GetScreenRect()
                    self.ayuda_toasterbox(x, y1, msj, t(u"Ayuda vipera", self.l))
        except: pass
        event.Skip()

    #def OnNoteBookModulos(self, event):
        #funcion = self.m_choice_funciones.GetStringSelection()
        ## Se ha elegido panel de funciones y no hay función seleccionada.
        #if len(funcion.strip()) == 0:
            #msj = u"Cree una función en 'Pestaña Funciones'," + \
                #u" haciendo click en 'Crear' y seleccionándola del combo"
            #msj = t(msj, self.l)
            #x, y, x1, y1 = self.GetScreenRect() 
            #self.ayuda_toasterbox(x, y1, msj, t(u"Ayuda vipera", self.l))
        #event.Skip()

    def salvar_ultimos_cambios(self):
        '''Salvar en estructura posibles cambios de módulos'''
        nombre_modulo = self.m_choice_modulos.GetStringSelection()
        if len(nombre_modulo.strip()) != 0: 
            # Salvar la posible función en edición.
            self.panel_param_func.salvar_funcion()
            # Salvar la posible clase en edición.
            clase_en_edicion = self.panel_def_clases.m_textCtrl_nc.GetValue()
            if len(clase_en_edicion.strip()) != 0: 
                # Salvar los posibles métodos en edición.
                self.panel_met_clases.salvar_info_metodo()
                self.panel_met_clases.salvar_param_metodo()
                self.panel_met_clases.salvar_editor_metodo()
                # Actualizamos datos de características de clase en estructura.
                # self.panel_met_clases.salvar_clase()
                # Definición de clase.
                self.panel_def_clases.salvar_def_clase()
                # Parámetros de instanciación de clase.
                self.panel_param_clases.salvar_param_inst_clase()
                # Atributos de clase.
                self.panel_atrib_clases.salvar_atrib_clase()
                # Propiedades de clase.
                self.panel_prop_clases.salvar_prop_clase()
                # Información de clase.
                self.panel_info_clases.salvar_info_clase()
                # Constantes de clase.
                self.panel_cte_clases.salvar_cte_clase()
                # Constructor de clase.
                self.panel_const_clases.salvar_constructor_clase()
                # Destructor de clase.
                self.panel_dest_clases.salvar_destructor_clase()

    def guardar_datos_modulo(self):
        '''Guarda módulo en estructura'''
        # Módulo actual.
        nombre_modulo = self.m_choice_modulos.GetStringSelection()
        if len(nombre_modulo.strip()) != 0: 
            # Datos del módulo.
            clases = self.panel_ogl.salvar_datos()
            funciones = self.panel_param_func.salvar_datos()
            documentacion = self.panel_doc.salvar_datos()
            imports = self.panel_imports.salvar_datos()
            registros = self.panel_registros.salvar_datos()
            constantes = self.panel_ctes.salvar_datos()
            codigo_modulo = self.panel_cod_prin.salvar_datos()
            # Guardamos datos en sistema de gestión de módulos.
            indice = -1
            for modulo in self.lista_modulos:
                indice += 1
                if modulo[0] == nombre_modulo:
                    self.lista_modulos[indice][1][0] = documentacion
                    self.lista_modulos[indice][1][1] = imports
                    self.lista_modulos[indice][1][2] = registros
                    self.lista_modulos[indice][1][3] = constantes
                    self.lista_modulos[indice][1][4] = funciones
                    self.lista_modulos[indice][1][5] = clases
                    self.lista_modulos[indice][1][6] = codigo_modulo
                    break

    def OnSetFocusChoiceModulo(self, event):
        '''Event Handler para guardar datos del modulo'''
        # Salvamos posibles últimos cambios.
        self.salvar_ultimos_cambios()
        # Guardamos datos del módulo en estructura.
        self.guardar_datos_modulo()
        event.Skip()

    def limpiar_widget_modulo(self):
        '''Limpia todos los widgets que compone un módulo'''

        # #####################
        # Limpiamos constantes.
        # #####################

        self.panel_ctes.limpiar_widget()

        # ####################
        # Limpiamos registros.
        # ####################

        self.panel_registros.limpiar_widget()

        # ####################
        # Limpiamos registros.
        # ####################

        self.panel_imports.limpiar_widget()

        # ########################
        # Limpiamos documentación.
        # ########################

        self.panel_doc.limpiar_widget()

        # ###########################
        # Limpiamos código principal.
        # ###########################

        self.panel_cod_prin.limpiar_widget()

        # ####################
        # Limpiamos funciones.
        # ####################

        self.panel_param_func.limpiar_widget()
        # Limpiamos lista de funciones.
        self.m_choice_funciones.SetItems([])                       

        # #################
        # Limpiamos clases.
        # #################

        # Limpiamos panel de ogl.
        self.panel_ogl.limpiar_widget()
        # Limpiamos definición de clase.
        self.panel_def_clases.limpiar_widget()
        # Limpiamos parámetros de instanciación.
        self.panel_param_clases.limpiar_widget()
        # Limpiamos atributos.
        self.panel_atrib_clases.limpiar_widget()
        # Limpiamos métodos.
        self.panel_met_clases.limpiar_widget()
        # Limpiamos lista de métodos.
        self.panel_met_clases.m_metodos.SetItems([])
        # Limpiamos propiedades de clase.
        self.panel_prop_clases.limpiar_widget()
        # Limpiamos información de clase.
        self.panel_info_clases.limpiar_widget()
        # Limpiamos constantes.
        self.panel_cte_clases.limpiar_widget()
        # Limpiamos constructor y destructor.
        self.panel_const_clases.limpiar_widget()
        self.panel_dest_clases.limpiar_widget()

    def cargar_datos_modulo(self, x_modulos):
        '''Carga estructura de módulos'''
        self.lista_modulos = list(x_modulos) # Creamos una lista.
        self.recargar_modulos_en_choice()

    def salvar_datos_modulo(self):
        '''Devuelve estructura de módulos'''
        # Salvamos en la estructura las posibles modificaciones del usuario.
        self.salvar_ultimos_cambios()
        # Guardamos el módulo actual.
        self.guardar_datos_modulo()
        # Hacemos una copia de la lista.
        modulos = list(self.lista_modulos)
        # Quitamos los dividedshape de la estructura.
        for modulo in modulos:
            if len(modulo[1][5]) == 0: continue
            clases_modulo = modulo[1][5]['modelo']
            for clase in clases_modulo: clase[1] = None
        # Devolvemos estructura modificada.
        return modulos

    def cargar_modulo(self, nombre_modulo):
        '''Carga de un módulo pasado como parámetro'''
        # #######################################
        # Limpiamos todos los widgets del módulo.
        # #######################################

        self.limpiar_widget_modulo() 

        # #######################################
        # Cargamos datos del módulo seleccionado.
        # #######################################

        if len(nombre_modulo.strip()) == 0:
            return 
        else:
            # Identificamos el módulo y traspasamos datos
            # a los widgets y estructuras correspondientes.
            indice = -1
            for modulo in self.lista_modulos:
                indice += 1
                if modulo[0] == nombre_modulo:
                    # Recogemos datos de estructura.
                    documentacion = self.lista_modulos[indice][1][0]  
                    imports = self.lista_modulos[indice][1][1]
                    registros = self.lista_modulos[indice][1][2]
                    constantes = self.lista_modulos[indice][1][3]  
                    funciones = self.lista_modulos[indice][1][4]
                    clases = self.lista_modulos[indice][1][5]
                    codigo_modulo = self.lista_modulos[indice][1][6]
                    # Cargamos datos en widgets.
                    if len(documentacion) !=0: self.panel_doc.cargar_datos(documentacion)
                    if len(imports) !=0: self.panel_imports.cargar_datos(imports)
                    if len(registros) !=0: self.panel_registros.cargar_datos(registros)
                    if len(constantes) !=0: self.panel_ctes.cargar_datos(constantes)
                    self.panel_param_func.cargar_datos(funciones)
                    self.panel_ogl.cargar_datos(clases)
                    if len(codigo_modulo) !=0: self.panel_cod_prin.cargar_datos(codigo_modulo)
                    break        

    def OnElegirModulo(self, event):
        '''Event Handler para intercambio de módulos en el diseñador'''
        # Nombre del módulo.
        nombre_modulo = self.m_choice_modulos.GetStringSelection()
        self.cargar_modulo(nombre_modulo)

    def OnExpandirPanelIzqFunc(self, event):
        self.m_splitter_funcion.SetSashPosition(10000)

    def OnExpandirPanelDerFunc(self, event):
        self.m_splitter_funcion.SetSashPosition(20)

    def OnExpandirPanelIzq(self, event):
        self.m_splitter_clases.SetSashPosition(10000)

    def OnExpandirPanelDer(self, event):
        self.m_splitter_clases.SetSashPosition(20)

    def recargar_modulos_en_choice(self):
        '''Recarga de nombre de módulos en choice de módulos'''
        modulos = list()
        for modulo in self.lista_modulos:
            modulos.append(modulo[0])
        self.m_choice_modulos.SetItems(modulos)

    def existe_modulo(self, nombre_modulo):
        '''Devuelve True si nombre_modulo existe y False en caso contrario'''
        ret = False
        for modulo in self.lista_modulos:
            if modulo[0] is None: continue # Se borró.
            if modulo[0].strip() == nombre_modulo:
                ret = True
                break
        return ret

    def __pedir_datos(self, opcion = 0):
        '''Pedir datos mediante un wx.TextEntryDialog.'''
        if opcion == 0: 
            cabecera = t(u'Crear módulo', self.l)
            cadena = t(u'Nombre del nuevo módulo: ', self.l)
        if opcion == 1: 
            cabecera = t(u'Renombrar módulo', self.l)
            cadena = t(u'Nuevo nombre del módulo: ', self.l)
        dialogo = wx.TextEntryDialog(self, cadena, cabecera)
        dialogo.Show()
        if dialogo.ShowModal() == wx.ID_OK:
            ret = dialogo.GetValue()
        else:
            # Botón cancelar. Nada que devolver.
            ret = None
        # Limpiamos memoria y nos vamos.
        dialogo.Destroy()
        return ret       

    def OnCrearModulo(self, event):
        '''Crear un módulo'''
        # Pedimos nombre del módulo.
        i = self.__pedir_datos()
        if i is None: return
        nom_mod = unicode(i).strip() 
        if len(nom_mod) == 0: return
        # ¿Existe el módulo?
        if self.existe_modulo(nom_mod): 
            wx.MessageBox(t(u"El módulo ya existe", self.l), t(u"Atención", self.l), wx.OK)
            return 
        # Creamos módulo.
        self.lista_modulos.append([nom_mod,[ {}, {}, {}, {}, {}, {}, {}]])
        # Lo incluimos en el componente gráfico.
        self.m_choice_modulos.Append(nom_mod)

    def OnRenombrarModulo(self, event):
        '''Manejador de eventos para renombrar un módulo'''
        modulo_actual = self.m_choice_modulos.GetStringSelection()
        if len(modulo_actual.strip()) == 0:
            wx.MessageBox(t(u"Seleccione el módulo a renombrar", self.l), t(u"Atención", self.l), wx.OK)
            return 
        # Pedimos nombre de módulo.
        i = self.__pedir_datos(1)
        if i is None: return
        nom_mod = unicode(i).strip()
        if len(nom_mod) == 0: return
        # Buscamos...
        indice = -1
        centinela = True
        # Comprobamos que el nombre del módulo no exista.
        for modulo in self.lista_modulos:
            if modulo[0].strip() == nom_mod:
                msj = t(u"El módulo ya existe", self.l) 
                wx.MessageBox(msj, t(u"Atención", self.l), wx.OK)
                return
        # Intentamos renombrar...
        for modulo in self.lista_modulos:
            indice += 1
            if modulo[0].strip() == modulo_actual:
                # Cambiamos en estructura.
                self.lista_modulos[indice][0] = nom_mod
                centinela = False
                break
        if centinela:
            wx.MessageBox(t(u"El módulo no existe", self.l),t(u"Atención", self.l),wx.OK)
        else:
            # Cambiamos en choice.
            j = self.m_choice_modulos.GetCurrentSelection() 
            self.m_choice_modulos.SetString(j, nom_mod)  
            self.m_choice_modulos.SetSelection(j)

    def OnEliminarModulo(self, event):
        '''Manejador de eventos para eliminar un módulo'''
        modulo_actual = self.m_choice_modulos.GetStringSelection()
        if len(modulo_actual.strip()) == 0:
            wx.MessageBox(t(u"Seleccione el módulo a eliminar", self.l), t(u"Atención", self.l), wx.OK)
            return 
        # Buscamos...
        indice = -1
        centinela = True
        for modulo in self.lista_modulos:
            indice += 1
            if modulo[0].strip() == modulo_actual:
                # Cambiamos en estructura.
                msj = t(u"¿Eliminar el módulo?", self.l)
                k = wx.MessageBox(msj, t(u"Atención", self.l), wx.YES_NO)
                if k != wx.YES: break
                self.lista_modulos.remove(modulo)
                # Limpiamos módulo.
                self.limpiar_widget_modulo()
                centinela = False
                break
        if centinela:
            wx.MessageBox(t(u"El módulo no se eliminó", self.l),t(u"Atención", self.l),wx.OK)
        else:
            # Cambiamos en choice.
            j = self.m_choice_modulos.GetCurrentSelection() 
            self.m_choice_modulos.Delete(j) 
            wx.MessageBox(t(u"El módulo se eliminó", self.l),t(u"Atención", self.l),wx.OK)

class panel_package(panel_p):
    '''Panel de packages del proyecto'''
    def __init__(self, parent, panel_principal, idioma = None):
        '''Constructor de clase'''        
        panel_p.__init__(self, parent, None, idioma)
        # Referencia al panel principal
        self.panel_principal = panel_principal
        # Binding al terminar de editar un elemento.
        self.m_treeCtrl_packages.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnEditarEndEtiqueta)

    # ##########################
    # Event handlers de edición.
    # ##########################

    def OnEditarEndEtiqueta(self, event):
        '''Edición de nombres de módulos ó carpetas'''
        ret = event.GetLabel()
        if ret.count("/") != 0 or ret.count("\\") != 0: 
            wx.MessageBox(t(u"Barras y contrabarras no son permitidas.", self.l),t(u"Atención", self.l),wx.OK)
            event.Veto()

    # Hacemos override del método de petición de datos, 
    # para incluir los módulos actuales del proyecto.

    def pedir_datos(self, cad):
        '''Pedir datos mediante un wx.TextEntryDialog.'''
        ret = ''
        if cad == 'modulo': cadena = t(u'Nombre del módulo nuevo: ', self.l)
        if cad == 'directorio': cadena = t(u'Nombre del directorio nuevo: ', self.l)
        if cad == 'root': cadena = t(u'Nombre del directorio padre: ', self.l)
        if cad in ['root', 'directorio']:
            dialogo = wx.TextEntryDialog(self, cadena, t('Datos', self.l))
            dialogo.Show()
            if dialogo.ShowModal() == wx.ID_OK:
                ret = dialogo.GetValue()
            else:
                # Botón cancelar. Nada que devolver.
                ret = None
        if cad in ['modulo']:
            # Buscamos los módulos.
            lista_modulos = list()
            for i in self.panel_principal.lista_modulos:
                # Cada nombre del módulo lo añadimos a la lista.
                lista_modulos.append(i[0])
            dialogo = dialogo_package_p(self, lista_modulos, self.l)
            if dialogo.ShowModal() == wx.ID_OK:
                ret = dialogo.m_comboBox_modulos.GetValue()
            else:
                # Botón cancelar. Nada que devolver.
                ret = None
        # Limpiamos memoria y nos vamos.
        dialogo.Destroy()
        if ret is not None:
            # No se puede incluir el carácter "/" ó "\".
            if ret.count("/") != 0 or ret.count("\\") != 0: 
                wx.MessageBox(t(u"Barras y contrabarras no son permitidas.", self.l),t(u"Atención", self.l) ,wx.OK)
                ret = None
        # Devolvemos resultado.
        return ret

class panel_editor(panel_codigo_p):
    '''Panel de código Python generado'''
    def __init__(self, parent, panel_principal, idioma = None):
        '''Constructor de clase'''        
        panel_codigo_p.__init__(self, parent, proy_por_defecto['f_cod'],
                                proy_por_defecto['f_sal'],
                                proy_por_defecto['f_err'],
                                proy_por_defecto['interprete'],
                                proy_por_defecto['lanzadera'],
                                idioma)
        # Referencia al panel principal.
        self.panel_principal = panel_principal

    def generar_codigo_python(self):
        '''Método para generar código Python de un módulo'''
        # Llamamos a la función de datos y obtenemos 
        # el código Python equivalente.
        # codigo = f_codigo_modulo(self.panel_principal, idioma = self.l)
        a = codigo_modulo_actual(self.panel_principal, idioma = self.l)
        codigo = a.codigo_python_generado()
        del a
        if codigo is not None:
            # Incluimos el código en el editor.
            self.set_codigo(codigo)    

class panel_pyshell(wx.Panel):
    '''Panel de Pyshell'''
    def __init__(self, parent):
        '''Constructor de clase'''        
        wx.Panel.__init__(self, parent)
        pyshell = py.Shell(self, style = wx.SP_LIVE_UPDATE)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(pyshell, 1, wx.EXPAND | wx.ALL, 10)
        self.SetSizer(sizer)

    
class barra_estado(wx.StatusBar):
    '''Barra de estado del frame principal'''
    def __init__(self, parent, idioma = None):
        '''Constructor de clase'''        
        wx.StatusBar.__init__(self, parent, -1)
        # Configuramos 3 campos.
        self.SetFieldsCount(3)
        # Configuramos el ancho relativo de los campos
        self.SetStatusWidths([-2, -1, -2])
        self.sizeChanged = False
        self.Bind(wx.EVT_SIZE, self.OnSize)
        # self.Bind(wx.EVT_IDLE, self.OnIdle)
        # El campo 0 tendrá información del proyecto actual
        self.SetStatusText(t(u"No hay proyectos abiertos", idioma), 0)
        # El campo 1 tendrá información de la aplicación y versión
        self.SetStatusText(t(u"Aplicación y versión", idioma), 1)
        # El campo 3 tendrá un reloj.
        self.timer = wx.PyTimer(self.Notify)
        self.timer.Start(1000)
        self.Notify()

    def Notify(self):
        '''Actualización de hora en barra de estado'''
        t = time.localtime(time.time())
        st = time.strftime("%d-%b-%Y   %I:%M:%S", t)
        self.SetStatusText(st, 2)

    def OnSize(self, event):
        '''Cambio de tamaño'''
        self.sizeChanged = True

    # def OnIdle(self, evt):
    #     if self.sizeChanged:
    #         self.Reposition()

    def get_texto_statusbar(self, campo = 0):
        '''Devolvemos texto de campo "campo"'''
        return self.GetStatusText(campo)
    
    def set_texto_statusbar(self, texto, campo):
        '''Modificamos texto de campo "campo"'''
        self.SetStatusText(texto, campo)
    
class frame_principal(wx.Frame):
    '''Frame principal de la aplicación'''
    def __init__(self, datos_conf, idioma = None):
        '''Constructor de clase'''
        titulo = t(u'vipera, un diseñador de aplicaciones para Python', idioma) #  datos_conf['titulo_aplic']
        wx.Frame.__init__(self, None, -1, title = titulo, size = wx.Size(1024,768))
        # Idioma.
        self.l = idioma
        # Barra de estado.
        self.barra_estado = barra_estado(self, idioma)
        aux = '%s %s (%s)' % (proy_por_defecto['NOMBRE_APLIC'], proy_por_defecto['VERSION'], proy_por_defecto['FECHA_CREACION'])
        self.barra_estado.set_texto_statusbar(aux, 1)
        self.SetStatusBar(self.barra_estado)
        # Centramos la ventana principal.
        self.Center(wx.CENTER_ON_SCREEN)
        # [16-08-2011] Incluimos ChoiceBook para elegir entre vipera y pyShell.
        self.choiceBook = wx.Choicebook(self, -1, style = wx.CHB_TOP|wx.FULL_REPAINT_ON_RESIZE )
        # Creamos el panel.
        self.panel = panel_principal(self.choiceBook, datos_conf, self.l)
        # Pyshell
        self.pyshell = panel_pyshell(self.choiceBook)
        # Panel de ingeniería inversa
        self.ing_inversa = panel_ing_inversa(self.choiceBook, datos_conf, idioma = self.l)
        # Panel de configuración de la aplicación
        self.conf_vipera = panel_conf_vipera_p(self.choiceBook, self, idioma = self.l)
        # Panel de gestión de proyectos (Nuevo, Abrir, Salvar)
        self.gestion_proy = panel_oper_proy_p(self.choiceBook , idioma = self.l)
        # Incluimos los paneles como hojas del choiceBook.
        self.choiceBook.AddPage(self.gestion_proy, t(u"Gestión de proyectos", self.l))
        self.choiceBook.AddPage(self.panel, t(u"Diseñador", self.l))
        self.choiceBook.AddPage(self.ing_inversa, t(u"Ingeniería inversa", self.l))        
        self.choiceBook.AddPage(self.pyshell, u"Python Shell")
        self.choiceBook.AddPage(self.conf_vipera, t(u"Configuración de la aplicación", self.l))
        # Creamos sizer.
        sizer = wx.BoxSizer(wx.VERTICAL)
        # Barras de herramientas.
        self.barra1 = wx.BoxSizer(wx.HORIZONTAL)
        # Incluimos barra1 en el sizer.        
        sizer.Add(self.barra1, 0, wx.EXPAND, 5)
        # Incluimos choicebook en el sizer.
        sizer.Add(self.choiceBook, 1, wx.EXPAND)
        # Añadimos el sizer al panel padre.
        self.SetSizer(sizer)
        sizer.Layout()